#include "ScriptDocumentOperation.h"

using namespace simodo;

/// \brief Имена модулей, подключаемых для семантического анализа по умолчанию
/// \todo Полагаю, это должно быть где-то в другом месте. Возможно, в main.cpp?
const std::vector<std::string> & PRELOAD_ANALYZE_MODULES = { "math", };

std::string ScriptDocumentOperation::getInitialContractFile() const
{
    const fs::path initial_contracts_path = _factory.grammar_dir()
                                            / fs::path(module::INITIAL_CONTRACTS_DIR)
                                            / fs::path(module::INITIAL_CONTRACTS_FILE);

    return fs::exists(initial_contracts_path) ? initial_contracts_path.string() : "";
}

std::pair<std::shared_ptr<module::ModuleManagement>, bool> ScriptDocumentOperation::executeModuleManager(
        inout::InputStreamSupplier_interface & input_supplier,
        inout::Reporter_abstract & reporter,
        interpret::SemanticDataCollector_interface & semantic_data_collector,
        interpret::InterpretType interpret_type,
        const std::string & initial_contract_file
) const {

    const std::vector<std::string> & semantics_places = { _factory.semantics_dir() };
    const std::vector<std::string> & hard_module_places = { _factory.modules_dir() };
    const std::vector<std::string> & grammar_places = { _factory.grammar_dir() };

    bool preview = interpret_type == interpret::InterpretType::Preview;

    auto mm = std::make_shared<module::ModuleManagement>(
            reporter,
            input_supplier,
            semantics_places,
            hard_module_places,
            grammar_places,
            interpret_type,
            semantic_data_collector,
            std::cerr,
            initial_contract_file,
            preview // debug_mode
    );

    bool ok = mm->execute(_doc.file_name(), PRELOAD_ANALYZE_MODULES);

    return { mm, ok };
}

bool ScriptDocumentOperation::analyze(const std::u16string & text, inout::Reporter_abstract & reporter)
{
    SemanticDataCollector semantic_data;
    const std::string initial_contract_file = getInitialContractFile();

    const auto [mm, ok] = executeModuleManager(
            *getInputSupplier(text),
            reporter,
            semantic_data,
            interpret::InterpretType::Analyzer,
            _doc.file_name() == initial_contract_file ? "" : initial_contract_file
    );

    if (not ok) { 
        // Фетисов: Могут быть обнаружены ошибки в семантике, но при этом какие-то данные 
        // могут всё равно обновиться
        if (semantic_data.declared().size() < _semantic_data.declared().size()
         && semantic_data.used().size() < _semantic_data.used().size())
            return true;
    }

    semantic_data.swap(_semantic_data);

    _files = mm->files();

    /// \note Фетисов: Код внутренних (internal) функций хранится в module::ModuleManagement,
    /// поэтому его необходимо сохранять до следующего вызова analyze. 
    /// \todo Фетисов: Код получился тяжеловатым, желательно переписать.
    _module_management_last_instance = mm;

    findCurrentFileIndex();

    return true;
}

void ScriptDocumentOperation::findCurrentFileIndex() {
    for (_current_file_index = 0; _current_file_index < _files.size(); ++_current_file_index) // NOLINT(*-too-small-loop-variable)
        if (_doc.file_name() == _files[_current_file_index])
            break;

    if (_current_file_index == _files.size()) {
        std::string files_string;
        for (const auto & _file : _files)
            files_string += "\t'" + _file + "'\n";
        _doc.server().log().error("Не удалось определить индекс файла '" + _doc.file_name() + "' среди этих:",
                                  files_string);
        _current_file_index = 0;
    }
}